package csa

import (
	"git.sr.ht/~mil/mobroute/src/db"
	"git.sr.ht/~mil/mobroute/src/logger"
	"git.sr.ht/~mil/mobroute/src/utilfuncs"
)

// ExecuteCSAMainLoop is the main logic loop for the CSA algorithm
// returns a map containing SIDU-to-connectionptr AND another map
// for SIDU-to-arrivaltime
func ExecuteCSAMainLoop(
	conns *[]db.Connection,
	transfers *[]db.Transfer,
	stopWalkcostsOrigin map[string]uint,
	originTimeSeconds, implicitTransferSeconds, maxTransferSeconds,
	maxNTransfers uint,
) map[string]ArrivalDetails {
	logger.Info("CSA Main Loop Begin")
	defer logger.Info("CSA Main Loop Complete")

	var (
		maxNTrips               = maxNTransfers + 1
		transfersMap            = buildTransfersMap(transfers)
		mapStopToArrivalDetails = InitializeMapStopToArrivalDetails(
			utilfuncs.MapValueAdd(stopWalkcostsOrigin, originTimeSeconds),
		)
	)
	for _, c := range *conns {
		var (
			fromConnection = mapStopToArrivalDetails[c.FromSSIDU].Connection

			connPossibleDirect            = fromConnection == nil || fromConnection.TripID == c.TripID
			connPossibleTransferConnected = TransferPossibleBetweenConnections(
				transfersMap, mapStopToArrivalDetails[c.FromSSIDU].Connection,
				&c, implicitTransferSeconds, maxTransferSeconds,
			)
			connPossible = connPossibleDirect || connPossibleTransferConnected

			connOriginDepartsLater        = c.FromST >= mapStopToArrivalDetails[c.FromSSIDU].Time
			connDestinationArrivesEarlier = c.ToST < mapStopToArrivalDetails[c.ToSSIDU].Time
		)
		if connOriginDepartsLater && connDestinationArrivesEarlier && connPossible {
			cc := c

			newNTransfers := func() int {
				if connPossibleDirect {
					return int(utilfuncs.MaxInt(1, mapStopToArrivalDetails[c.FromSSIDU].NTrips))
				} else if mapStopToArrivalDetails[c.FromSSIDU].NTrips < maxNTrips {
					return int(mapStopToArrivalDetails[c.FromSSIDU].NTrips + 1)
				} else {
					return -1
				}
			}()

			if newNTransfers == -1 {
				continue
			}

			mapStopToArrivalDetails[c.ToSSIDU] = ArrivalDetails{
				Time:       c.ToST,
				Connection: &cc,
				NTrips:     uint(newNTransfers),
			}
			for transferToSSIDU, transferToStopCostSeconds := range transfersMap[c.ToSSIDU] {
				var (
					transferFromTime = c.FromST
					transferToST     = c.ToST + transferToStopCostSeconds
					departsLater     = transferFromTime > mapStopToArrivalDetails[c.FromSSIDU].Time
					arrivesEarlier   = transferToST < mapStopToArrivalDetails[transferToSSIDU].Time
					tPoss            = transferToStopCostSeconds < maxTransferSeconds
				)
				if departsLater && arrivesEarlier && tPoss && mapStopToArrivalDetails[c.FromSSIDU].NTrips < maxNTrips {
					mapStopToArrivalDetails[transferToSSIDU] = ArrivalDetails{
						Time:       transferToST,
						Connection: &cc,
						NTrips:     mapStopToArrivalDetails[c.FromSSIDU].NTrips + 1,
					}
				}
			}
		}
	}
	logger.Warnf("Completed main CSA loop %v", mapStopToArrivalDetails)
	return mapStopToArrivalDetails
}
